package com.dkts.oil.strtree;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.dkts.oil.config.OilConfig;
import com.dkts.oil.converter.GeometryConverter;
import com.dkts.oil.mysql.entity.OilObstacle;
import com.dkts.oil.util.FileToStringUtils;
import com.dkts.oil.util.GeoAreaUtil;
import com.dkts.oil.util.gis.HexUtil;

import lombok.extern.slf4j.Slf4j;
import org.locationtech.jts.geom.*;
import org.locationtech.jts.index.strtree.STRtree;
import org.locationtech.jts.io.ParseException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.*;
import java.util.*;

@Component
@Slf4j
public class GeoJsonSTRtreeIndex {

    public static STRtree strTreeBuild = null;
    public static STRtree mountainTreeBuild = null;
    public static STRtree trainSTreeBuild = null;

    @Autowired
    private OilConfig oilConfig;

    // 从 GeoJSON 文件加载并构建 STRtree
    public void loadBuildJsonFile() throws Exception {
        long start = System.currentTimeMillis();
        strTreeBuild = new STRtree();
        int i=1;
        try (BufferedReader reader = new BufferedReader(new FileReader(new File(oilConfig.getBuildDir())))) {
            String line;

            while ((line = reader.readLine()) != null ){
                JSONObject feature = JSONObject.parseObject(line);

                JSONObject geometry = feature.getJSONObject("geometry");
                JSONObject properties = feature.getJSONObject("properties");

                String name = properties.getString("name");
                Integer _label = properties.getInteger("_label");
                if (_label == null){
                    _label = -1;
                }

                // 清理属性
                properties.remove("name");
                properties.remove("path");
                properties.remove("_label");
                properties.remove("layer");
                properties.remove("_id");
                properties.remove("osm_id");
                properties.remove("code");

                Geometry geom = GeometryConverter.geoJsonToGeometry(geometry.toJSONString());
                Point centroid = geom.getCentroid();
                double[] area = GeoAreaUtil.calculateAreaInSquareMeters(geom);

                OilObstacle obstacle = new OilObstacle(
                        i + 1,
                        name,
                        _label,
                        null,
                        HexUtil.geoToH3Address(centroid.getX(), centroid.getY(), 6),
                        JSONObject.toJSONString(properties),
                        centroid,
                        area[1], area[0], geom);

                strTreeBuild.insert(geom.getEnvelopeInternal(), obstacle);
                i++;
            }

        }

        try (BufferedReader reader = new BufferedReader(new FileReader(new File(oilConfig.getWaterDir())))) {
            String line;

            while ((line = reader.readLine()) != null ){
                JSONObject feature = JSONObject.parseObject(line);
                JSONObject geometry = feature.getJSONObject("geometry");
                JSONObject properties = feature.getJSONObject("properties");

                String name = properties.getString("name");
                Integer _label = null;
                String _labelStr = properties.getString("fclass");
                if (_labelStr == null){
                    System.out.println("没有找到类型：-----------"+line);
                    continue;
                }
                //河岸
                if (_labelStr.equals("riverbank")){
                    _label = -2;

                 //水库
                }else if (_labelStr.equals("reservoir")){
                    _label = -3;
                 //水域
                }else if (_labelStr.equals("water")){
                    _label = -4;
                 //湿地
                }else if (_labelStr.equals("wetland")){
                    _label = -5;
                }else {
                    continue;
                }

                // 清理属性
                properties.remove("name");
                properties.remove("path");
                properties.remove("_label");
                properties.remove("layer");
                properties.remove("_id");
                properties.remove("osm_id");
                properties.remove("code");

                Geometry geom = GeometryConverter.geoJsonToGeometry(geometry.toJSONString());
                Point centroid = geom.getCentroid();
                double[] area = GeoAreaUtil.calculateAreaInSquareMeters(geom);

                OilObstacle obstacle = new OilObstacle(
                        i + 1,
                        name,
                        _label,
                        null,
                        HexUtil.geoToH3Address(centroid.getX(), centroid.getY(), 6),
                        JSONObject.toJSONString(properties),
                        centroid,
                        area[1], area[0], geom);

                strTreeBuild.insert(geom.getEnvelopeInternal(), obstacle);
                i++;
            }

        }
        strTreeBuild.build();
        log.info("✅ STRtree 构建完成，用时 {} ms", System.currentTimeMillis() - start);

        // 序列化到缓存文件
//        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(oilConfig.getRtreeCacheFileURL()))) {
//            oos.writeObject(strTreeBuild);
//            log.info("✅ STRtree 已序列化到：{}", oilConfig.getRtreeCacheFileURL());
//        }
//        System.exit(-1);
    }

    @PostConstruct
    public void init() {
        new Thread(() -> {
            try {
                File file = new File(oilConfig.getRtreeCacheFileURL());
//                if (!file.exists() || file.length() == 0) {
//                    log.info("⚠️ 缓存建筑物文件不存在，开始重新构建 STRtree");
                    loadBuildJsonFile();
//                } else {
//                    try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) {
//                        strTreeBuild = (STRtree) ois.readObject();
//                        log.info("✅ 成功从缓存文件加载 建筑物： STRtree");
//                    }
//                }
            } catch (Exception e) {
                log.error("❌ 加载 建筑物 STRtree 时发生异常", e);
            }
        }).start();

        new Thread(() -> {
            try {
                File file = new File(oilConfig.getMountainFileUrl());
                if (file.exists() && file.length() > 0) {
                    log.info("⚠️ 缓存山峰文件不存在，开始重新构建 STRtree");
                    loadMountainTreeBuildGeoJsonFile();
                }
//                else {
//                    try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) {
//                        strTreeBuild = (STRtree) ois.readObject();
//                        log.info("✅ 山峰成功从缓存文件加载 STRtree");
//                    }
//                }
            } catch (Exception e) {
                log.error("❌ 山峰加载 STRtree 时发生异常", e);
            }
        }).start();


        new Thread(() -> {
            try {
                File file = new File(oilConfig.getMountainTrainUrl());
                if (file.exists() && file.length() > 0) {
                    log.info("⚠️ 火车站中心点不存在，开始重新构建 STRtree");
                    loadTrainUrlGeoJsonFile();
                }
//                else {
//                    try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) {
//                        strTreeBuild = (STRtree) ois.readObject();
//                        log.info("✅ 山峰成功从缓存文件加载 STRtree");
//                    }
//                }
            } catch (Exception e) {
                log.error("❌ 火车站中心加载 STRtree 时发生异常", e);
            }
        }).start();
    }

    private void loadTrainUrlGeoJsonFile() throws ParseException {
        long start = System.currentTimeMillis();
        trainSTreeBuild = new STRtree();
        String geoJsonContent = FileToStringUtils.txt2String(new File(oilConfig.getMountainTrainUrl()));
        JSONObject jsonObject = JSONObject.parseObject(geoJsonContent);
        JSONArray features = jsonObject.getJSONArray("features");

        log.info("🔄loadTrainUrlGeoJsonFile  开始加载 GeoJSON 文件，共 {} 个要素", features.size());

        for (int i = 0; i < features.size(); i++) {
            JSONObject feature = features.getJSONObject(i);
            JSONObject geometry = feature.getJSONObject("geometry");
            JSONObject properties = feature.getJSONObject("properties");

            String name = properties.getString("name");
            Integer _label = properties.getInteger("_label");

            // 清理属性
            properties.remove("path");
            properties.remove("_label");
            properties.remove("layer");

            Geometry geom = GeometryConverter.geoJsonToGeometry(geometry.toJSONString());
            Point centroid = geom.getCentroid();
            double[] area = GeoAreaUtil.calculateAreaInSquareMeters(geom);

            OilObstacle obstacle = new OilObstacle(
                    i + 1,
                    name,
                    _label,
                    null,
                    HexUtil.geoToH3Address(centroid.getX(), centroid.getY(), 6),
                    JSONObject.toJSONString(properties),
                    centroid,
                    area[1], area[0], geom);

            trainSTreeBuild.insert(geom.getEnvelopeInternal(), obstacle);
        }
        trainSTreeBuild.build();
        log.info("✅ 火车站 STRtree 构建完成，用时 {} ms", System.currentTimeMillis() - start);
    }

    public void loadMountainTreeBuildGeoJsonFile() throws Exception {
        long start = System.currentTimeMillis();
        mountainTreeBuild = new STRtree();
        String geoJsonContent = FileToStringUtils.txt2String(new File(oilConfig.getMountainFileUrl()));
        JSONObject jsonObject = JSONObject.parseObject(geoJsonContent);
        JSONArray features = jsonObject.getJSONArray("features");

        log.info("🔄 开始加载 GeoJSON 文件，共 {} 个要素", features.size());

        for (int i = 0; i < features.size(); i++) {
            JSONObject feature = features.getJSONObject(i);
            JSONObject geometry = feature.getJSONObject("geometry");
            JSONObject properties = feature.getJSONObject("properties");

            String name = properties.getString("name");
            Integer _label = properties.getInteger("_label");

            // 清理属性
            properties.remove("path");
            properties.remove("_label");
            properties.remove("layer");

            Geometry geom = GeometryConverter.geoJsonToGeometry(geometry.toJSONString());
            Point centroid = geom.getCentroid();
            double[] area = GeoAreaUtil.calculateAreaInSquareMeters(geom);

            OilObstacle obstacle = new OilObstacle(
                    i + 1,
                    name,
                    _label,
                    null,
                    HexUtil.geoToH3Address(centroid.getX(), centroid.getY(), 6),
                    JSONObject.toJSONString(properties),
                    centroid,
                    area[1], area[0], geom);

            mountainTreeBuild.insert(geom.getEnvelopeInternal(), obstacle);
        }
        mountainTreeBuild.build();
        log.info("✅ 山峰 STRtree 构建完成，用时 {} ms", System.currentTimeMillis() - start);
    }

    // 查询相交的要素
    public static List<OilObstacle> queryByPolygon(Geometry targetGeom, STRtree strTree) {
        List<?> candidates = strTree.query(targetGeom.getEnvelopeInternal());
        List<OilObstacle> results = new ArrayList<>();
        for (Object obj : candidates) {
            OilObstacle oilObstacle = (OilObstacle) obj;
            if (oilObstacle.getGeometry().intersects(targetGeom)) {
                results.add(oilObstacle);
            }
        }
        return results;
    }

    // main 方法用于测试
    public static void main(String[] args) throws Exception {
        String cacheFilePath = "D:\\dkSpace\\油库规划\\strTreeCache\\rtree-all01.ser";
        String geoJsonFilePath = "D:\\dkSpace\\油库规划\\障碍物新的\\全国建筑物.geojson.geojsonl.json";
        String waterJSON = "D:\\dkSpace\\油库规划\\障碍物新的\\全国水系.geojson.geojsonl.json";

        OilConfig oilConfig = new OilConfig();
        oilConfig.setRtreeCacheFileURL(cacheFilePath);
        oilConfig.setBuildDir(geoJsonFilePath);
        oilConfig.setWaterDir(waterJSON);

        GeoJsonSTRtreeIndex index = new GeoJsonSTRtreeIndex();
        index.oilConfig = oilConfig;

        File file = new File(cacheFilePath);
        if (!file.exists() || file.length() == 0) {
            log.info("⚠️ 未检测到缓存文件，开始首次加载...");
            index.loadBuildJsonFile();
        }

        // 加载缓存
        long start = System.currentTimeMillis();
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file))) {
            strTreeBuild = (STRtree) ois.readObject();
            log.info("✅ 成功加载 STRtree，用时 {} ms", System.currentTimeMillis() - start);
        }

        // 测试查询
        start = System.currentTimeMillis();
        GeometryFactory factory = new GeometryFactory();
        Coordinate[] coords = new Coordinate[]{
                new Coordinate(111.951993, 29.374644),
                new Coordinate(111.9623796, 29.3740191),
                new Coordinate(111.9616501, 29.3651165),
                new Coordinate(111.9512645, 29.3657413),
                new Coordinate(111.951993, 29.374644) // 闭合
        };
        Polygon queryPolygon = factory.createPolygon(coords);

        List<OilObstacle> matched = index.queryByPolygon(queryPolygon, strTreeBuild);
        List<Integer> matchedIds = new ArrayList<>();
        for (OilObstacle o : matched) {
            matchedIds.add(o.getId());
        }
        System.exit(-1);

        log.info("🔍 查询完成，用时 {} ms，命中区域 ID：{}", System.currentTimeMillis() - start, matchedIds);
    }
}
